﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;


namespace FileTreeSpace
{
    public delegate void DirCreateEventHandler (string newPathName, string newLeafName);
    public delegate void DirRenameEventHandler (string oldPathName, string oldLeafName, string newLeafName);
    public delegate void DirDeleteEventHandler (string fullPathName);
    public delegate void FileCreateEventHandler (string fullName, string baseName, long size, DateTime dateModified, FileAttributes newAttributes);
    public delegate void FileChangeEventHandler (string fullName, long size, FileAttributes attributes);
    public delegate void FileRenameEventHandler (string oldFullPathName, string newFullPathName, string newBaseName);
    public delegate void FileDeleteEventHandler (string fullPathName);


    public class FileItem
    {
        public long Size { get; internal set; }  // File size requires 64 bits.
        public string Name { get; internal set; }
        public FileHash Hash { get; internal set; }
        public DateTime DateModified { get; internal set; }
        public FileAttributes Attributes { get; internal set; }

        internal FileItem (string name, long size, DateTime dateModified, FileAttributes attributes)
        { this.Name = name; this.Size = size; Hash = null; this.DateModified = dateModified; this.Attributes = attributes; }

        public Boolean IsHashed { get { return Hash != null; } }
    }


    // Vector of file names (directory component).
    public class FileList
    {
        private List<FileItem> items;

        internal FileList () { items = new List<FileItem> (); }

        internal void Add (string name, long size, DateTime dateModified, FileAttributes newAttribs)
        { items.Add (new FileItem (name, size, dateModified, newAttribs)); }

        internal void Delete (int index)
        { items.RemoveAt (index); }

        public FileItem this[int index]
        { get { return items[index]; } internal set { items[index] = value; } }

        public int Count { get { return items.Count; } }

        public IEnumerator GetEnumerator ()
        {
            for (int i = 0; i < items.Count; ++i)
                yield return items[i];
        }
    }


    // Vector of directory names.
    public class DirList
    {
        private List<DirItem> list;
        internal DirList () { list = new List<DirItem> (); }

        internal void Add (DirItem index) { list.Add (index); }
        public int Count { get { return list.Count; } }
        public DirItem this[int index] { get { return list[index]; } }
        internal void RemoveAt (int i) { list.RemoveAt (i); }
    }


    public class DirItem
    {
        private string name;
        private DirList dirs;
        private FileList files;

        public string Name { get { return name; } internal set { name = value; } }
        public DirList Dirs { get { return dirs; } }
        public FileList Files { get { return files; } }
        public DirItem this[int index] { get { return dirs[index]; } }

        internal DirItem (string newName) { init (newName); }


        internal void init (string newName)
        {
            name = newName;
            dirs = new DirList ();
            files = new FileList ();
        }


        internal int loadTree ()
        {
            string s = name + "\\";

            DirectoryInfo di = new DirectoryInfo (s);
            if (di.Exists)
                return 1 + buildDown (di);
            else
                return 0;
        }


        // Build recursively.  Directory 'di' already confirmed traversable.
        internal int buildDown (DirectoryInfo di)
        {
            int result = 0;

            FileInfo[] fis = di.GetFiles ();
            foreach (FileInfo currentFile in fis)
                files.Add (currentFile.Name, currentFile.Length, currentFile.LastWriteTime, currentFile.Attributes);

            DirectoryInfo[] dis = di.GetDirectories ();
            foreach (DirectoryInfo di2 in dis)
                if ((di2.Attributes & (FileAttributes.System | FileAttributes.ReparsePoint)) == 0)
                {
                    DirItem newDir = new DirItem (di2.Name);
                    dirs.Add (newDir);
                    result += 1 + newDir.buildDown (di2);
                }

            return result;
        }
    }

}
